import 'dart:io';
import 'package:dio/dio.dart';
import 'package:path_provider/path_provider.dart';

class utilHttp {
  // 单例模式
  static final utilHttp _instance = utilHttp._internal();

  // Dio 实例
  late Dio dio;

  // 请求取消令牌集合，key 为自定义请求标识
  final Map<String, CancelToken> _cancelTokens = {};

  // 工厂构造函数返回单例
  factory utilHttp() => _instance;

  // 内部构造函数
  utilHttp._internal() {
    BaseOptions options = BaseOptions(
      baseUrl: 'https://yourapi.com', // 默认基础地址，可修改
      connectTimeout: const Duration(seconds: 10), // 连接超时
      receiveTimeout: const Duration(seconds: 10), // 接收超时
    );

    dio = Dio(options);

    // 添加请求拦截器
    dio.interceptors.add(InterceptorsWrapper(
      onRequest: (options, handler) {
        // 设置公共请求头，如 Token
        options.headers['Authorization'] = 'Bearer your_token_here';
        return handler.next(options); // 继续请求
      },
      onResponse: (response, handler) {
        // 可统一处理成功响应
        return handler.next(response); // 继续响应
      },
      onError: (e, handler) {
        // 统一错误处理
        print('请求出错：${e.message}');
        return handler.next(e); // 继续传递错误
      },
    ));
  }

  /// ------------------------ GET 请求 ------------------------

  // JSON 格式 GET 请求，支持传 tag 用于取消请求
  Future<Response> getJson(String path,
      {Map<String, dynamic>? params, String? tag}) async {
    CancelToken? cancelToken;
    if (tag != null) {
      cancelToken = CancelToken();
      _cancelTokens[tag] = cancelToken;
    }
    try {
      final response = await dio.get(
        path,
        queryParameters: params,
        cancelToken: cancelToken,
      );
      return response;
    } finally {
      if (tag != null) _cancelTokens.remove(tag); // 请求结束移除cancelToken
    }
  }

  // x-www-form-urlencoded 格式 GET 请求
  Future<Response> getForm(String path,
      {Map<String, dynamic>? params, String? tag}) async {
    dio.options.contentType = Headers.formUrlEncodedContentType;
    return getJson(path, params: params, tag: tag);
  }

  /// ------------------------ POST 请求 ------------------------

  // JSON 格式 POST 请求
  Future<Response> postJson(String path, Map<String, dynamic> data,
      {String? tag}) async {
    dio.options.contentType = Headers.jsonContentType;
    CancelToken? cancelToken;
    if (tag != null) {
      cancelToken = CancelToken();
      _cancelTokens[tag] = cancelToken;
    }
    try {
      final response = await dio.post(
        path,
        data: data,
        cancelToken: cancelToken,
      );
      return response;
    } finally {
      if (tag != null) _cancelTokens.remove(tag);
    }
  }

  // x-www-form-urlencoded 格式 POST 请求
  Future<Response> postForm(String path, Map<String, dynamic> data,
      {String? tag}) async {
    dio.options.contentType = Headers.formUrlEncodedContentType;
    return postJson(path, data, tag: tag);
  }

  // multipart/form-data 格式 POST 请求（用于上传）
  Future<Response> postMultipart(String path, FormData data,
      {String? tag}) async {
    dio.options.contentType = Headers.multipartFormDataContentType;
    CancelToken? cancelToken;
    if (tag != null) {
      cancelToken = CancelToken();
      _cancelTokens[tag] = cancelToken;
    }
    try {
      final response = await dio.post(
        path,
        data: data,
        cancelToken: cancelToken,
      );
      return response;
    } finally {
      if (tag != null) _cancelTokens.remove(tag);
    }
  }

  /// ------------------------ 文件上传 ------------------------

  // 单文件上传，支持取消请求
  Future<Response> uploadSingleFile(String path, String filePath,
      {String fileKey = 'file', String? tag}) async {
    dio.options.contentType = Headers.multipartFormDataContentType;
    MultipartFile file = await MultipartFile.fromFile(filePath,
        filename: filePath.split('/').last);
    FormData formData = FormData.fromMap({fileKey: file});
    return postMultipart(path, formData, tag: tag);
  }

  // 多文件上传，支持取消请求
  Future<Response> uploadMultipleFiles(String path, List<String> filePaths,
      {String fileKey = 'files', String? tag}) async {
    dio.options.contentType = Headers.multipartFormDataContentType;
    List<MultipartFile> files = await Future.wait(filePaths.map((filePath) {
      return MultipartFile.fromFile(filePath,
          filename: filePath.split('/').last);
    }));
    FormData formData = FormData.fromMap({fileKey: files});
    return postMultipart(path, formData, tag: tag);
  }

  /// ------------------------ 文件下载 ------------------------

  Future<void> downloadFile(String url, String filename,
      {void Function(int received, int total)? onProgress, String? tag}) async {
    Directory dir = await getApplicationDocumentsDirectory();
    String savePath = '${dir.path}/$filename';

    CancelToken? cancelToken;
    if (tag != null) {
      cancelToken = CancelToken();
      _cancelTokens[tag] = cancelToken;
    }

    try {
      await dio.download(
        url,
        savePath,
        onReceiveProgress: onProgress,
        cancelToken: cancelToken,
      );
      print('文件已下载到：$savePath');
    } finally {
      if (tag != null) _cancelTokens.remove(tag);
    }
  }

  /// ------------------------ 请求取消 ------------------------

  // 取消指定 tag 的请求
  void cancelRequest(String tag) {
    if (_cancelTokens.containsKey(tag)) {
      _cancelTokens[tag]?.cancel("请求取消：$tag");
      _cancelTokens.remove(tag);
    }
  }

  // 取消全部请求
  void cancelAllRequests() {
    _cancelTokens.forEach((key, token) {
      token.cancel("全部请求取消");
    });
    _cancelTokens.clear();
  }
}







//✅ 示例用法：
//dart
//复制
//编辑
//final http = HttpUtil();
//
//// GET 请求（JSON）
//http.getJson('/user/info');
//
//// POST 请求（JSON）
//http.postJson('/login', {'username': 'aaa', 'password': '123456'});
//
//// POST 表单请求
//http.postForm('/submit', {'name': '张三', 'age': '18'});
//
//// 单文件上传
//http.uploadSingleFile('/upload', '/path/to/image.jpg');
//
//// 多文件上传
//http.uploadMultipleFiles('/upload/batch', ['/a.jpg', '/b.jpg']);
//
//// 文件下载
//http.downloadFile('https://xxx.com/file.apk', 'demo.apk', onProgress: (r, t) {
//print('下载进度：${(r / t * 100).toStringAsFixed(2)}%');
//});
// --------------取消请求-----------------------------
// // 发起请求时传入 tag
// HttpUtil().getJson('/test', tag: 'request1');
//
// // 取消请求
// HttpUtil().cancelRequest('request1');
//
// // 取消所有请求
// HttpUtil().cancelAllRequests();
// --------------异步请求------------------------
//✅ 示例一：GET 请求（异步）
//dart
//复制
//编辑
void fetchUserData() async {
 try {
   final http = utilHttp();
   // 发起 GET 请求
   Response response = await http.getJson('/user/profile', params: {
     'id': 123,
   });

   // 打印响应数据
   print('用户数据: ${response.data}');
 } catch (e) {
   print('请求出错: $e');
 }
}
//✅ 示例二：POST 请求（application/json）
//dart
//复制
//编辑
//void loginUser() async {
//  try {
//    // 准备 JSON 参数
//    Map<String, dynamic> data = {
//      'username': 'john',
//      'password': '123456',
//    };
//
//    // 发起 POST 请求
//    Response res = await http.postJson('/login', data);
//
//    // 响应处理
//    print('登录成功: ${res.data}');
//  } catch (e) {
//    print('登录出错: $e');
//  }
//}
//✅ 示例三：POST 请求（form-data）
//dart
//复制
//编辑
//void submitForm() async {
//  try {
//    Response res = await http.postForm('/submit', {
//      'name': 'Alice',
//      'age': '25',
//    });
//    print('提交成功: ${res.data}');
//  } catch (e) {
//    print('提交失败: $e');
//  }
//}
//✅ 示例四：单文件上传
//dart
//复制
//编辑
//void uploadImage() async {
//  try {
//    String filePath = '/path/to/your/image.png';
//
//    Response res = await http.uploadSingleFile('/upload', filePath);
//    print('上传成功: ${res.data}');
//  } catch (e) {
//    print('上传失败: $e');
//  }
//}
//✅ 示例五：取消请求（基于 CancelToken）
//dart
//复制
//编辑
//CancelToken cancelToken = CancelToken();
//
//void fetchWithCancel() async {
//  try {
//    Response res = await http.getJson('/long-request',
//        params: {}, cancelToken: cancelToken);
//
//    print('结果: ${res.data}');
//  } catch (e) {
//    if (CancelToken.isCancel(e)) {
//      print('请求被取消');
//    } else {
//      print('请求出错: $e');
//    }
//  }
//}
//
//// 例如在用户关闭页面时调用
//void cancelRequestManually() {
//  cancelToken.cancel('用户取消请求');
//}
//⚠️ 你要确保 HttpUtil 中的 GET/POST 方法支持 CancelToken 传参，如果你需要我帮你改造支持，请告诉我。